/* Copyright (c) 2006, 2009, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
 
package com.cburch.draw.model;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.geom.Line2D;
import java.util.List;

import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.UItools.GraphicsUtil;
import com.cburch.logisim.util.IntegerFactory;

abstract class Poly extends DrawingMember {
    private Location[] locations;
    private int[] xs;
    private int[] ys;
    private Bounds bounds;
    private int strokeWidth;
    private Color strokeColor;
    
    public Poly(List locations) {
        Location[] locs = new Location[locations.size()];
        locs = (Location[]) locations.toArray(locs);
        
        this.locations = locs;
        xs = new int[locs.length];
        ys = new int[locs.length];
        recomputeBounds();
        strokeWidth = 1;
        strokeColor = Color.BLACK;
    }
    
    public Object getValue(Attribute attr) {
        if(attr == DrawingAttribute.STROKE_COLOR) {
            return strokeColor;
        } else if(attr == DrawingAttribute.STROKE_WIDTH) {
            return IntegerFactory.create(strokeWidth);
        } else {
            return null;
        }
    }
    
    public void setAttrValue(Attribute attr, Object value) {
        if(attr == DrawingAttribute.STROKE_COLOR) {
            strokeColor = (Color) value;
        } else if(attr == DrawingAttribute.STROKE_WIDTH) {
            strokeWidth = ((Integer) value).intValue();
        }
    }
    
    public Bounds getBounds() {
        return bounds;
    }
    
    public void translate(int dx, int dy) {
        Location[] locs = locations;
        for(int i = 0; i < locs.length; i++) {
            locs[i] = locs[i].translate(dx, dy);
        }
        recomputeBounds();
    }
    
    public void getHandles(List dest) {
        dest.clear();
        Location[] locs = locations;
        for(int i = 0; i < locs.length; i++) {
            dest.add(locs[i]);
        }
    }
    
    public void getHandles(List dest, int handleIndex, int dx, int dy) {
        dest.clear();
        Location[] locs = locations;
        for(int i = 0; i < locs.length; i++) {
            dest.add(i == handleIndex ? locs[i].translate(dx, dy) : locs[i]);
        }
    }
    
    public void moveHandle(int index, int dx, int dy) {
        Location[] locs = locations;
        if(index >= 0 && index < locs.length) {
            locs[index] = locs[index].translate(dx, dy);
            recomputeBounds();
        } else {
            throw new IndexOutOfBoundsException("" + index);
        }
    }
    
    public boolean canInsertHandle(int handleIndex) {
        return true;
    }
    
    public boolean canDeleteHandle(int handleIndex) {
        return locations.length >= 4;
    }
    
    public void insertHandle(int handleIndex) {
        Location[] oldLocs = locations;
        Location[] newLocs = new Location[oldLocs.length + 1];
        for(int i = 0; i < newLocs.length; i++) {
            if(i < handleIndex) {
                newLocs[i] = oldLocs[i];
            } else if(i == handleIndex) {
                Location a = oldLocs[i];
                Location b = oldLocs[(i + oldLocs.length - 1) % oldLocs.length];
                int x = (a.getX() + b.getX()) / 2;
                int y = (a.getY() + b.getY()) / 2;
                newLocs[i] = Location.create(x, y);
            } else {
                newLocs[i] = oldLocs[i - 1];
            }
        }
        locations = newLocs;
        xs = new int[newLocs.length];
        ys = new int[newLocs.length];
        recomputeBounds();
    }
    
    public void deleteHandle(int handleIndex) {
        Location[] oldLocs = locations;
        Location[] newLocs = new Location[oldLocs.length - 1];
        for(int i = 0; i < newLocs.length; i++) {
            if(i < handleIndex) {
                newLocs[i] = oldLocs[i];
            } else {
                newLocs[i] = oldLocs[i + 1];
            }
        }
        locations = newLocs;
        xs = new int[newLocs.length];
        ys = new int[newLocs.length];
        recomputeBounds();
    }

    protected void recomputeBounds() {
        Location[] locs = locations;
        Bounds bds = Bounds.create(locs[0]);
        for(int i = 1; i < locs.length; i++) bds = bds.add(locs[i]);
        bounds = strokeWidth < 2 ? bds : bds.expand(strokeWidth / 2);
    }
    
    public void draw(Graphics g, int xOffs, int yOffs) {
        int[] x = xs;
        int[] y = ys;
        Location[] locs = locations;
        for(int i = 0; i < xs.length; i++) {
            x[i] = locs[i].getX() + xOffs;
            y[i] = locs[i].getY() + yOffs;
        }
        draw(g, x, y);
    }
    
    public void draw(Graphics g, int xOffs, int yOffs,
            int handleIndex, int handleDx, int handleDy) {
        int[] x = xs;
        int[] y = ys;
        Location[] locs = locations;
        for(int i = 0; i < xs.length; i++) {
            x[i] = locs[i].getX() + xOffs;
            y[i] = locs[i].getY() + yOffs;
        }
        x[handleIndex] += handleDx;
        y[handleIndex] += handleDy;
        draw(g, x, y);
    }
    
    protected boolean setForStroke(Graphics g) {
        if(strokeWidth > 0) {
            GraphicsUtil.switchToWidth(g, strokeWidth);
            g.setColor(strokeColor);
            return true;
        } else {
            return false;
        }
    }
    
    protected int getStrokeWidth() {
        return strokeWidth;
    }
    
    protected double ptBorderDistSq(Location loc) {
        int xq = loc.getX();
        int yq = loc.getY();
        Location[] locs = locations;
        double min = Double.MAX_VALUE;
        if(locs.length > 0) {
            Location first = locs[0];
            int x0 = first.getX();
            int y0 = first.getY();
            for(int i = 1; i < locs.length; i++) {
                Location next = locs[i];
                int x1 = next.getX();
                int y1 = next.getY();
                double d = Line2D.ptLineDistSq(x0, y0, x1, y1, xq, yq);
                if(d < min) min = d;
                x0 = x1;
                y0 = y1;
            }
        }
        return min;
    }

    public abstract boolean contains(Location loc);
    protected abstract void draw(Graphics g, int[] x, int[] y);
}